package com.system.io.netty;

import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.*;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.CharsetUtil;
import org.junit.Test;

import java.io.IOException;
import java.net.InetSocketAddress;

/**
 * @description: nettyAPI
 * @author: 81271
 * @date: 2020-08-04 22:24
 */
public class MyNetty {


    @Test
    public void myByteBuf() {
        //byteBuf 是有两个索引的  读写索引
//        ByteBuf buf = ByteBufAllocator.DEFAULT.buffer(8, 20);

        //pool
//        ByteBuf buf = UnpooledByteBufAllocator.DEFAULT.heapBuffer(8, 20);
        ByteBuf buf = PooledByteBufAllocator.DEFAULT.heapBuffer(8, 20);
        print(buf);


        buf.writeBytes(new byte[]{1,2,3,4});
        print(buf);

        buf.writeBytes(new byte[]{1,2,3,4});
        print(buf);

        buf.writeBytes(new byte[]{1,2,3,4});
        print(buf);

        buf.writeBytes(new byte[]{1,2,3,4});
        print(buf);

        buf.writeBytes(new byte[]{1,2,3,4});
        print(buf);
//
//        buf.writeBytes(new byte[]{1,2,3,4});
//        print(buf);
    }

    public static void print(ByteBuf buf) {
        System.out.println("buf.isReadable()      ：" + buf.isReadable());
        System.out.println("buf.readerIndex()     ：" + buf.readerIndex());
        System.out.println("buf.readableBytes()   ：" + buf.readableBytes());
        System.out.println("buf.isWritable()      ：" + buf.isWritable());
        System.out.println("buf.writerIndex()     ：" + buf.writerIndex());
        System.out.println("buf.writableBytes()   ：" + buf.writableBytes());
        System.out.println("buf.capacity()        ：" + buf.capacity());
        System.out.println("buf.maxCapacity()     ：" + buf.maxCapacity());
        System.out.println("buf.isDirect()        ：" + buf.isDirect());//分配方式  true 为堆外内存分配  false为堆内
        System.out.println("------------------------");
    }

    @Test
    public void loopExecutor() throws IOException {
        //group 线程池
        NioEventLoopGroup selector = new NioEventLoopGroup(1);
        selector.execute(() -> {
            try {
                for(;;) {
                    System.out.println("hello001");
                    Thread.sleep(1000);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        selector.execute(() -> {
            try {
                for(;;) {
                    System.out.println("hello001");
                    Thread.sleep(1000);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        System.in.read();

    }

    @Test
    public void clientMode() throws InterruptedException {
        NioEventLoopGroup thread = new NioEventLoopGroup(1);

        NioSocketChannel client = new NioSocketChannel();

        thread.register(client);//epoll ctl(5, ADD, 3)

        ChannelPipeline pipeline = client.pipeline();//可以对读取到的fd 进行处理
        pipeline.addLast(new MyInHandler());

        //reactor 异步的特征
        ChannelFuture connect = client.connect(new InetSocketAddress("192.168.13.10", 9999));
        ChannelFuture connectFuture = connect.sync();

        ByteBuf buf = Unpooled.copiedBuffer("hello server!".getBytes());
        ChannelFuture send = client.writeAndFlush(buf);
        send.sync();


        connectFuture.channel().closeFuture().sync();//关注连接关闭

        System.out.println("client over..........");
    }


    @Test
    public void nettyClient() throws InterruptedException {
        NioEventLoopGroup group = new NioEventLoopGroup(1);
        Bootstrap bs = new Bootstrap();
        ChannelFuture connect = bs.group(group)
                .channel(NioSocketChannel.class)
//                .handler(new ChannelInit())
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        socketChannel.pipeline().addLast(new MyInHandler());
                    }
                })
                .connect(new InetSocketAddress("192.168.13.10", 9999));

        Channel client = connect.sync().channel();

        ByteBuf buf = Unpooled.copiedBuffer("hello server!".getBytes());
        ChannelFuture send = client.writeAndFlush(buf);
        send.sync();

        client.closeFuture().sync();

    }

    @Test
    public void ServerMode() throws InterruptedException {
        NioEventLoopGroup thread = new NioEventLoopGroup(1);

        NioServerSocketChannel server = new NioServerSocketChannel();

        thread.register(server);

        ChannelPipeline pipeline = server.pipeline();
        pipeline.addLast(new MyAcceptHandler(thread, new ChannelInit()));

        ChannelFuture bind = server.bind(new InetSocketAddress("192.168.1.3", 9999));
        bind.sync().channel().closeFuture().sync();

    }

    @Test
    public void nettyServer () throws InterruptedException {
        NioEventLoopGroup group = new NioEventLoopGroup(1);
        ServerBootstrap sb = new ServerBootstrap();
        ChannelFuture bind = sb.group(group, group)
                .channel(NioServerSocketChannel.class)
//                .childHandler(new ChannelInit())
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {
                        nioSocketChannel.pipeline().addLast(new MyInHandler());
                    }
                })
                .bind(new InetSocketAddress(9999));

        bind.sync().channel().closeFuture().sync();
    }


}

class MyAcceptHandler extends ChannelInboundHandlerAdapter{

    private final EventLoopGroup thread;
    private final ChannelHandler handler;

    public MyAcceptHandler(EventLoopGroup thread, ChannelHandler handler) {
        this.thread = thread;
        this.handler = handler;
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("active......");
    }

    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("register......");
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        //listen socket   accept client
        //socket          R/W

        SocketChannel client = (SocketChannel) msg;

        client.pipeline().addLast(handler); //1.client::pipeline [ChannelInit]

        thread.register(client);



    }
}

//为啥要有ChannelInit
// 因为每个客户端进来都要在自己的pipeline里面添加一个MyInHandler  但是代码里你只new了一份
//所以程序认为你的MyInHandler 必须为一个单例 不然容易出现数据的脏处理  ，但是我的MyInHandler  肯定不能为单例的 有很多属性需要处理
@ChannelHandler.Sharable
class ChannelInit extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        Channel client = ctx.channel();
        client.pipeline().addLast(new MyInHandler()); //2.client::pipeline [ChannelInit, MyInHandler]
        ctx.pipeline().remove(this);
    }
}



class MyInHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("active......");
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf buf = (ByteBuf) msg;
//        CharSequence charSequence = buf.readCharSequence(buf.readableBytes(), CharsetUtil.UTF_8);
        //read write 方法会改变对应的索引  get set 方法不会
        CharSequence charSequence = buf.getCharSequence(0, buf.readableBytes(), CharsetUtil.UTF_8);
        System.out.println(charSequence);

        ctx.writeAndFlush(buf);

    }
}
